#include <buola/cv/barcode/scan.h>

namespace buola { namespace cvi { namespace barcode {

void scan_line(const uint8_t *pData,int pInc,int pCount,std::vector<int> &pWidths)
{
    int lY1Sign=0;
    int lY2Sign=0;
    pWidths.reserve(pCount);
    pWidths.clear();
    const int lY1MinThresh=4; //this is the min threshold defined in zbar
    int lY1Thresh=lY1MinThresh;
    int lCurEdge=0;
    int lLastEdge=0;
    int lLastWidth=0;
    int y0_0=*pData;
    int y1_0=0,y1_1=0;
    int y2_0=0,y2_1=0;
    for(int x=1;x<pCount;x++)
    {
        pData+=pInc;
        y1_1=y1_0;
        y1_0=(((int)*pData-y0_0)*cEWMA) >> cFixedExp;
        y0_0+=y1_0;
        y2_1=y2_0;
        y2_0=y1_0-y1_1;

        if(!y2_0) continue;
        
        if(!lY2Sign)
        {
            lY2Sign=y2_0;
            continue;
        }
        
        //no zero crossing in 2nd derivative, continue
        if((lY2Sign^y2_0)>=0) continue;
        lY2Sign=y2_0;

        //zero crossing means local min/max in 1st derivative - could be edge
        int lTmpThresh=lY1Thresh;
        if(!lLastWidth)
        {
            lTmpThresh=lY1MinThresh;
        }
        else if(lTmpThresh>lY1MinThresh)
        {
            /* slowly return threshold to min */
            unsigned dx=(x<<cFixedExp)-lLastEdge;
            unsigned long t=lTmpThresh*dx;
            t/=lLastWidth;
            t/=cThreshFade;
            lTmpThresh-=t;
            if(lTmpThresh<lY1MinThresh)
            {
                lTmpThresh=lY1MinThresh;
                lY1Thresh=lY1MinThresh;
            }
        }

        if(abs(y1_1)<lTmpThresh) continue;

        //check for sign change in first derivative
        //can't use XOR here because lY1Sign might be 0
        bool lY1Rev=(lY1Sign>0)?y1_1<0:y1_1>0;
        if(lY1Rev)
        {
            lLastWidth=lCurEdge-lLastEdge;
            pWidths.push_back(lLastWidth);
            lLastEdge=lCurEdge;
        }

        if(lY1Rev||(abs(lY1Sign)<abs(y1_1))) 
        {
            lY1Sign=y1_1;

            /* adaptive thresholding */
            /* start at multiple of new min/max */
            lY1Thresh=(abs(y1_1) * cThreshInit + cRound) >> cFixedExp;

            if(lY1Thresh<lY1MinThresh)
                lY1Thresh=lY1MinThresh;

            //update current edge
            int d = y2_0 - y2_1;
            lCurEdge = 1 << cFixedExp;
            if(!d)
                lCurEdge >>= 1;
            else if(y2_0)
                /* interpolate zero crossing */
                lCurEdge -= ((y2_0 << cFixedExp) + 1) / d;
            lCurEdge += x << cFixedExp;
        }
    }

    if(!lY1Sign) return;

    if(lCurEdge>lLastEdge)
        pWidths.push_back(lCurEdge-lLastEdge);
    pWidths.push_back((pCount<<cFixedExp)-lCurEdge);
}
    
/*namespace barcode*/ } /*namespace cvi*/ } /*namespace buola*/ }
